package org.newlucene.core.index;

import java.io.IOException;

import org.newlucene.core.store.Directory;
import org.newlucene.core.store.InputStream;

final class SegmentTermInfosIndex
{
	private InputStream input;
	private FieldInfos fieldInfos;
	int size;
	int position = -1;

	private Term term = new Term("", "");
	private TermInfo termInfo = new TermInfo();

	long indexPointer = 0;
	Term prev;

	private char[] buffer = {};

	// for caching term info index
	Term[] indexTerms;
	TermInfo[] indexInfos;
	long[] indexPointers;

	SegmentTermInfosIndex(Directory dir, String segment, FieldInfos fis) throws IOException 
	{
		input = dir.openFile(segment + ".tii");
		fieldInfos = fis; 
		size = input.readInt();

		indexTerms = new Term[size];
		indexInfos = new TermInfo[size];
		indexPointers = new long[size];

		try 
		{
			for (int i = 0; this.next(); i++) 
			{
				indexTerms[i] = this.term();
				indexInfos[i] = this.termInfo();
				indexPointers[i] = this.indexPointer;
			}
		}
		finally 
		{
			input.close();
		}
	}

	/** Increments the enumeration to the next element.  True if one exists.*/
	private final boolean next() throws IOException 
	{
		if (position++ >= size-1) 
		{
			term = null;
			return false;
		}

		prev = term;
		term = readTerm();

		termInfo.docCount = input.readVInt();	  // read doc freq
		termInfo.freqPointer += input.readVLong();	  // read freq pointer    
		indexPointer += input.readVLong();	  // read index pointer

		return true;
	}

	private final Term readTerm() throws IOException 
	{
		int start = input.readVInt();
		int length = input.readVInt();
		int totalLength = start + length;
		if (buffer.length < totalLength)
		{
			growBuffer(totalLength);
		}
		
		input.readChars(buffer, start, length);
		return new Term(fieldInfos.getFieldInfo(input.readVInt()).name, new String(buffer, 0, totalLength));
	}

	private final void growBuffer(int length) 
	{
		buffer = new char[length];
		for (int i = 0; i < term.text.length(); i++)  // copy contents
		{
			buffer[i] = term.text.charAt(i);
		}
	}

	/** Returns the offset of the greatest index entry which is less than term.*/
	public final int seekIndexOffset(Term term) throws IOException 
	{
		int lo = 0;					  // binary search indexTerms[]
		int hi = indexTerms.length - 1;

		while (hi >= lo) 
		{
			int mid = (lo + hi) >> 1;
			int delta = term.compareTo(indexTerms[mid]);
			if (delta < 0)
			{
				hi = mid - 1;
			}
			else if (delta > 0)
			{
				lo = mid + 1;
			}
			else
			{
				return mid;
			}
		}
		return hi;
	}
	
	/** Returns the current Term in the enumeration.
    	Initially invalid, valid after next() called for the first time.*/
	private final Term term() 
	{
		return term;
	}

	/** Returns the current TermInfo in the enumeration.
    Initially invalid, valid after next() called for the first time.*/
	private final TermInfo termInfo() 
	{
		return new TermInfo(termInfo.docCount, termInfo.freqPointer);
	}
}
